home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / Utilities / Random / Confusion / source / Confusion.c next >
Text File  |  1992-09-18  |  14KB  |  578 lines

  1. /**********
  2.  * Sound Confusion
  3.  *
  4.  *        by Bernie Bernstein
  5.  *        9/11/92
  6.  ***********/
  7.  
  8.  
  9. #include    <GestaltEqu.h>
  10. #include    <Memory.h>
  11. #include    <OSEvents.h>
  12. #include    <Sound.h>
  13. #include    <SoundInput.h>
  14. #include    <StdIO.h>
  15. #include    <Types.h>
  16.  
  17. #include "Confusion.h"
  18.  
  19. /* how much memory should the program reserve after making the buffers? */
  20. #define kExtraMem        0x30000
  21.  
  22.  
  23. /*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
  24. The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
  25. pointing at a SoundHeader Record and not an 'snd ' resource header. */
  26. #define kHeaderSize        20
  27.  
  28. typedef struct {
  29.     short        OnOff;
  30.     short         Level;
  31.     short         Length;
  32. } Level;
  33.  
  34.  
  35. Boolean    inStereo;
  36. short gThreshold;
  37. long gBuffSize;
  38. SCGlobals *globs;
  39. BufInfo gExtraBuffer;
  40.  
  41.  
  42. /***
  43.  * BufPlay
  44.  *        This routine takes an snd buffer and a sound channel and turns the information
  45.  *        into a bufferCmd to the channel.
  46.  ***/
  47. void BufPlay (Handle Buf, SndChannelPtr    channel)
  48. {
  49.     SndCommand        localSndCmd;
  50.     OSErr            err;
  51.     
  52.     localSndCmd.cmd = bufferCmd;
  53.     localSndCmd.param1 = 0;
  54.     localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
  55.     
  56.     err = SndDoCommand (channel, &localSndCmd, false);
  57.     if (err != noErr)
  58.         AlertUser(err, iPlaying);
  59.     return;
  60. }
  61.  
  62.  
  63. /***
  64.  * SetUpSounds
  65.  *        SetUpSounds is a routine which takes a buffer handle, sound headers size and
  66.  *        sample rate value and turns it into a snd buffer with the proper header.
  67.  *        It then returns the buffer handle back to the caller.
  68.  ***/
  69. Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
  70. {
  71.     OSErr        err;
  72.     short        dummy;
  73.     
  74.     Buf = NewHandle(gBuffSize);
  75.     if ((err = MemError()) != noErr || Buf == nil)
  76.         AlertUser(err, iSetupBuffer);
  77.     MoveHHi (Buf);
  78.     if ((err = MemError()) != noErr)
  79.         AlertUser(err, iSetupBuffer);
  80.     HLock (Buf);
  81.     if ((err = MemError()) != noErr)
  82.         AlertUser(err, iSetupBuffer);
  83.     
  84.     /* The call to SetupSndHeader is done twice in order to make sure that the 
  85.     Header size is set correctly.  */
  86.  
  87.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
  88.     if (err != noErr)
  89.         AlertUser(err, iSetupBuffer);
  90.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, gBuffSize - *HeaderSize, &dummy);
  91.     if (err != noErr)
  92.         AlertUser(err, iSetupBuffer);
  93.         
  94.     return (Buf);
  95. }
  96.  
  97. /***
  98.  * RndRange
  99.  *        Pick a random number from min to max.
  100.  ***/
  101. short RndRange(short min, short max)
  102. {
  103.     unsigned short r = (unsigned short)Random();
  104.     short d = max-min+1;
  105.     return r / (0x10000/d) + min;
  106. }
  107.  
  108.  
  109. /***
  110.  * PickPlayableBuffer
  111.  *        Return the index of a buffer which is ready to be played. It randomly
  112.  *        picks one from the playable buffers, that is, ones which have some
  113.  *        sound in them.
  114.  ***/
  115. short PickPlayableBuffer(BufInfo *buffers, short max)
  116. {
  117.     short rnd;
  118.     short i;
  119.  
  120.     for(rnd = RndRange(0,max-1), i=0; 
  121.             !buffers[rnd].playable; 
  122.             i++)
  123.         {
  124.         if (i>100)
  125.             return max+1;
  126.  
  127.         rnd = RndRange(0,max-1);
  128.         }
  129.     return rnd;
  130. }
  131.  
  132.  
  133. /***
  134.  * PickReadableBuffer
  135.  *        Return the index of a buffer which can read some new sounds
  136.  *        First it fills up each buffer, then it randomly picks new ones
  137.  *        once they are all full.
  138.  ***/
  139. short PickReadableBuffer(BufInfo *buffers, short max)
  140. {
  141.     short rnd;
  142.     short i;
  143.  
  144.     for(i=0; i<max; i++)
  145.         {
  146.         if (!buffers[i].playable)
  147.             return i;
  148.         }
  149.  
  150.     return RndRange(0,max-1);
  151. }
  152.  
  153.  
  154. #define Abs(x)        ((x<0)?-(x):x)
  155. #define Min(x,y)    ((x<y)?(x):(y))
  156. #define Max(x,y)    ((x<y)?(y):(x))
  157. #define BlankSpace    0x1000
  158.  
  159. /***
  160.  * TrimBuffer
  161.  *        If the buffer is silent, then return false. Otherwise, reduce the
  162.  *        buffer to just the parts that have sound.
  163.  ***/
  164. long TrimBuffer(Handle buffer, long headerlen)
  165. {
  166.     Boolean result = false;
  167.     long oldBuffSize = GetHandleSize(buffer);
  168.     long oldEnd = 0;
  169.     long oldStart = headerlen;
  170.     long newIndex = 0;
  171.     long copySize = 0;
  172.     long bufByte = 0;
  173.     Boolean firstTime = true;
  174.     Handle tmpbuffer = gExtraBuffer.buffer;
  175.  
  176.     /* copy header into new buffer */
  177.     BlockMove(*buffer, *tmpbuffer, headerlen);
  178.  
  179.     /* determine if the buffer was loud enough */
  180.     /* loud means that a byte in the buffer is far enough away from the */
  181.     /* center to get above the gThreshold */
  182.     for (bufByte=0; bufByte<oldBuffSize-headerlen; bufByte++)
  183.         {
  184.         short delta = 0x80 - (unsigned char)(*buffer)[headerlen + bufByte];
  185.         if (Abs(delta) << 1 > gThreshold)
  186.             {
  187.             /* the first time we hear something, save the previous BlankSpace bytes */
  188.             /* until now. */
  189.             if (firstTime)
  190.                 {
  191.                 /* If we are close to the beginning, copy from the beginning. */
  192.                 /* otherwise, copy blank space until this spot */
  193.                 if (bufByte > BlankSpace)
  194.                     oldStart = bufByte - BlankSpace;
  195.                 else
  196.                     oldStart = 0;
  197.                     
  198.                 /* If we are near the end, copy to the end. */
  199.                 /* otherwise, copy blank space after this spot */
  200.                 oldEnd = bufByte + BlankSpace;
  201.                 if (headerlen + oldEnd > oldBuffSize)
  202.                     oldEnd = oldBuffSize - headerlen;
  203.                 copySize = oldEnd - oldStart;
  204.  
  205.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  206.                 newIndex += copySize;
  207.                 bufByte = oldEnd;
  208.                 firstTime = false;
  209.                 }
  210.             else
  211.                 {
  212.                 /* After the first time, bufByte > BlankSpace. */
  213.                 oldStart = bufByte - BlankSpace;
  214.                 if (oldStart < oldEnd)
  215.                     oldStart = oldEnd+1;
  216.  
  217.                 oldEnd = bufByte + BlankSpace;
  218.                 if (headerlen + oldEnd > oldBuffSize)
  219.                     oldEnd = oldBuffSize - headerlen;
  220.                 copySize = oldEnd - oldStart;
  221.  
  222.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  223.                 newIndex += copySize;
  224.                 bufByte = oldEnd;
  225.                 }
  226.             result = true;
  227.             }
  228.         }
  229.     
  230.     if (result)
  231.         {
  232.         BlockMove(*tmpbuffer, *buffer, newIndex+headerlen);
  233.         return newIndex;
  234.         }
  235.     else
  236.         {
  237.         return 0L;
  238.         }
  239. }
  240.  
  241.  
  242.  
  243. #define TestBit(l,b)    ((l>>b)&0x01)
  244.  
  245. /***
  246.  * InitSoundConfusion
  247.  *        Allocate memory and initialize the sound driver
  248.  ***/
  249. void InitSoundConfusion(void)
  250. {
  251.     Level                myLevel;
  252.     OSErr                err;
  253.     long                feature;
  254.     BufInfo                *Buffers;
  255.     short                i;
  256.     short                numBuffers;
  257.     short                meterState;
  258.     SPBPtr                RecordRec;
  259.     Str255                settingStr;
  260.     long                tmplong;
  261.  
  262.     /* use Gestalt to make sure the app will work */
  263.     err = Gestalt(gestaltSoundAttr, &feature);
  264.     if (err == noErr)
  265.         {
  266.         if (!TestBit(feature, gestaltHasSoundInputDevice))
  267.             {
  268.             AlertUser(0, iNoInput);
  269.             ExitToShell();
  270.             }
  271.         else
  272.             inStereo = TestBit(feature, gestaltStereoCapability);
  273.         }
  274.     else
  275.         {
  276.         AlertUser(err, iGestaltFailed);
  277.         ExitToShell();
  278.         }
  279.     
  280.     
  281.     GetIndString(settingStr, rSettingStrings, iThreshold);
  282.     StringToNum(settingStr, &tmplong);
  283.     gThreshold = tmplong;
  284.  
  285.     GetIndString(settingStr, rSettingStrings, iBufferSize);
  286.     StringToNum(settingStr, &tmplong);
  287.     gBuffSize = tmplong;
  288.     
  289.     globs = (SCGlobals*)NewPtr(sizeof(SCGlobals));
  290.     if ((err = MemError()) != noErr || globs == nil)
  291.         {
  292.         AlertUser(err, iStarting);
  293.         return;
  294.         }
  295.     
  296.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  297.     err = SPBOpenDevice (nil, siWritePermission, &globs->SoundRefNum);
  298.     if (err != noErr)
  299.         {
  300.         AlertUser(err, iStarting);
  301.         return;
  302.         }
  303.  
  304.     // turn on sound metering
  305.     meterState = 1;                // turn it on
  306.     err = SPBSetDeviceInfo(globs->SoundRefNum, siLevelMeterOnOff, (char *)&meterState);
  307.     if (err != noErr)
  308.         Debugger();
  309.  
  310.     /* Get the sample rate information for the snd header */
  311.     err = SPBGetDeviceInfo (globs->SoundRefNum,siSampleRate, (Ptr) &globs->sampleRate);
  312.     if (err != noErr)
  313.         {
  314.         AlertUser(err, iStarting);
  315.         return;
  316.         }
  317.     
  318.     
  319.     /* build the RecordRec pointer and fill in the fields */
  320.     RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
  321.     if ((err = MemError()) != noErr || RecordRec == nil)
  322.         {
  323.         AlertUser(err, iStarting);
  324.         return;
  325.         }
  326.  
  327.     /* how many buffers can we make? (leave space for an extra buffer and then some memory) */
  328.     numBuffers = ((FreeMem() - kExtraMem) / gBuffSize) - 1;
  329.     if (numBuffers < 1)
  330.         {
  331.         AlertUser(0, iMemory);
  332.         return;
  333.         }
  334.     
  335.     Buffers = (BufInfo*)NewPtr(sizeof(BufInfo)*numBuffers);
  336.     if ((err = MemError()) != noErr || Buffers == nil)
  337.         {
  338.         AlertUser(err, iStarting);
  339.         return;
  340.         }
  341.         
  342.     globs->fullBuffer = false;
  343.     globs->loudEnough = false;
  344.     globs->bufferGettingFilled = 0;
  345.     globs->buffers = Buffers;
  346.     globs->numBuffers = numBuffers;
  347.     globs->RecordRec = RecordRec;
  348.     
  349.     /* build the snd buffers */
  350.     for (i=0; i<numBuffers; i++)
  351.         {
  352.         Buffers[i].buffer = SetUpSounds(Buffers[i].buffer, &Buffers[i].headerlength, globs->sampleRate);
  353.         Buffers[i].buffSize = gBuffSize - Buffers[i].headerlength;
  354.         Buffers[i].playable = false;
  355.         }
  356.  
  357.     gExtraBuffer.buffer = SetUpSounds(gExtraBuffer.buffer, &gExtraBuffer.headerlength, globs->sampleRate);
  358.  
  359.     RecordRec->inRefNum = globs->SoundRefNum;
  360.     RecordRec->count =  gBuffSize - Buffers[0].headerlength;
  361.     RecordRec->milliseconds = 0;
  362.     RecordRec->bufferLength = gBuffSize - Buffers[0].headerlength;
  363.     RecordRec->bufferPtr = (Ptr) ((*Buffers[0].buffer) + Buffers[0].headerlength);
  364.     RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
  365.     RecordRec->interruptRoutine = nil;
  366.     RecordRec->userLong = (long)globs;
  367.     RecordRec->error = 0;
  368.     RecordRec->unused1 = 0;
  369.     
  370.     
  371.     if (inStereo)
  372.         {
  373.         /* open the sound channel which I will need to play from */
  374.         globs->leftChan = nil;
  375.         err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
  376.         if (err != noErr)
  377.             {
  378.             AlertUser(err, iStarting);
  379.             return;
  380.             }
  381.             
  382.         /* open the sound channel which I will need to play from */
  383.         globs->rightChan = nil;
  384.         err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
  385.         if (err != noErr)
  386.             {
  387.             AlertUser(err, iStarting);
  388.             return;
  389.             }
  390.         }
  391.     else
  392.         {
  393.         /* open the sound channel which I will need to play from */
  394.         globs->leftChan = nil;
  395.         err = SndNewChannel (&globs->leftChan, sampledSynth, 0, nil);
  396.         if (err != noErr)
  397.             {
  398.             AlertUser(err, iStarting);
  399.             return;
  400.             }
  401.         }
  402.             
  403.     globs->firstTime = true;
  404.     
  405.     
  406. }
  407.  
  408.  
  409. /***
  410.  * ConfuseSound
  411.  *        This is called during idle time from the event loop.
  412.  *        It starts recording from a buffer and playing from another one.
  413.  ***/
  414. void ConfuseSound(void)
  415. {
  416.     short            dummy;
  417.     OSErr            err;
  418.     BufInfo            *Buffers;
  419.     short            i;
  420.     SndCommand        mycmd;
  421.     short            recordStat, MetLevel;
  422.     unsigned long    totalSampleRecord, TotalMilRecord, ActSampleRecord, ActMilRecord;
  423.     long            bufByte;
  424.     short            bufNum;
  425.     
  426.     Buffers = globs->buffers;
  427.     
  428.     /* get the loudness of the current input */
  429.     err = SPBGetRecordingStatus(globs->SoundRefNum, &recordStat, &MetLevel, &totalSampleRecord,
  430.                                 &ActSampleRecord, &TotalMilRecord, &ActMilRecord);
  431.     if (err != noErr)
  432.         AlertUser(err, iReadingLevel);
  433.  
  434.     if (globs->fullBuffer || globs->firstTime)
  435.         {
  436.         globs->fullBuffer = false;
  437.  
  438.         if (!globs->firstTime)
  439.             {
  440.             long newSize;
  441.             bufNum = globs->bufferGettingFilled;
  442.             newSize = TrimBuffer(Buffers[bufNum].buffer, Buffers[bufNum].headerlength);
  443.             Buffers[bufNum].playable = globs->loudEnough = (newSize > 0);
  444.             Buffers[bufNum].buffSize = newSize;
  445.  
  446.             /* if the buffer was loud enough, then prepare to record the next readable buffer */
  447.             if (globs->loudEnough)
  448.                 {
  449.                 short nextbuf = PickReadableBuffer(Buffers, globs->numBuffers);
  450.                 globs->bufferGettingFilled = nextbuf;
  451.                 globs->loudEnough = false;
  452.                 Buffers[nextbuf].playable = false;
  453.                 HUnlock(Buffers[nextbuf].buffer);
  454.                 SetHandleSize(Buffers[nextbuf].buffer, gBuffSize);
  455.                 MoveHHi(Buffers[nextbuf].buffer);
  456.                 HLock(Buffers[nextbuf].buffer);
  457.                 globs->RecordRec->bufferPtr = (*Buffers[nextbuf].buffer) + Buffers[nextbuf].headerlength;
  458.                 }
  459.             }
  460.                 
  461.         globs->firstTime = false;
  462.         /* start recording into the buffer asynchronously */
  463.         err = SPBRecord (globs->RecordRec, true); // start recording
  464.         if (err != noErr)
  465.             AlertUser(err, iRecording);
  466.         
  467.         /* pick a buffer to play from */    
  468.         i = PickPlayableBuffer(Buffers, globs->numBuffers);
  469.         if (i<=globs->numBuffers)
  470.             {
  471.             long bsize = Buffers[i].buffSize;
  472.             err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
  473.             if (err != noErr)
  474.                 AlertUser(err, iPlaying);
  475.             /* play it asynchronously */
  476.             BufPlay(Buffers[i].buffer, 
  477.                     inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
  478.             }
  479.         }
  480. }
  481.  
  482.  
  483. /***
  484.  * StopConfusion
  485.  *        Deallocate memory and close stuff.
  486.  ***/
  487. void StopConfusion(void)
  488. {
  489.     OSErr            err;
  490.     SndCommand        mycmd;
  491.     short            i;
  492.  
  493.     /* stop playing sounds now */
  494.     mycmd.cmd = quietCmd;
  495.     mycmd.param1 = 0;
  496.     mycmd.param2 = 0;
  497.     err = SndDoImmediate (globs->leftChan, &mycmd);
  498.     if (err != noErr)
  499.         AlertUser(err, iClosing);
  500.     if (inStereo)
  501.         {
  502.         err = SndDoImmediate (globs->leftChan, &mycmd);
  503.         if (err != noErr)
  504.             AlertUser(err, iClosing);
  505.         }
  506.  
  507.     /* kill the channel */
  508.     err = SndDisposeChannel (globs->leftChan,false);
  509.     if (err != noErr)
  510.         AlertUser(err, iClosing);
  511.     if (inStereo)
  512.         {
  513.         err = SndDisposeChannel (globs->rightChan,false);
  514.         if (err != noErr)
  515.             AlertUser(err, iClosing);
  516.         }
  517.  
  518.     /* close the input device */
  519.     SPBCloseDevice (globs->SoundRefNum);
  520.     
  521.     /* kill the snd buffers */
  522.     for (i=0; i<globs->numBuffers; i++)
  523.         {
  524.         HUnlock(globs->buffers[i].buffer);
  525.         DisposeHandle(globs->buffers[i].buffer);
  526.         }
  527.     HUnlock(gExtraBuffer.buffer);
  528.     DisposeHandle(gExtraBuffer.buffer);
  529.  
  530.     /* kill the buffers */
  531.     DisposePtr ((Ptr) globs->buffers);
  532.     DisposePtr ((Ptr) globs->RecordRec);
  533.     DisposePtr ((Ptr) globs);
  534. }
  535.  
  536.  
  537. /**********************************/
  538.  
  539. void DoAbout()
  540. {
  541.     Str31 numBufsStr;
  542.     Str31 numFilledStr;
  543.     short numFilled = 0;
  544.     short i;
  545.     short itemHit;
  546.     DialogPtr dlg;
  547.     
  548.     for (i=0; i<globs->numBuffers; i++)
  549.         {
  550.         if (globs->buffers[i].playable)
  551.             numFilled++;
  552.         }
  553.     NumToString((long)globs->numBuffers, numBufsStr);
  554.     NumToString((long)numFilled, numFilledStr);
  555.     ParamText(numBufsStr, numFilledStr, "", "");
  556.  
  557.     dlg = GetNewDialog(rAboutAlert, NULL, (WindowPtr)-1L);
  558.     ModalDialog(NULL, &itemHit);
  559.     DisposDialog(dlg);
  560. }
  561.  
  562.  
  563. /***
  564.  * MyRecComp
  565.  *
  566.  * This is the Completion Routine which is called every time the buffer,
  567.  * which is being recorded into, is full.
  568.  ***/
  569. pascal void MyRecComp (SPBPtr inParamPtr)
  570. {
  571.     SCGlobals *glob;
  572.     
  573.     glob = (SCGlobals*)inParamPtr->userLong;
  574.     glob->fullBuffer = true;
  575.  
  576.     return;
  577. }
  578.